﻿//#if _MSC_VER >= 1600
//#pragma execution_character_set("utf-8")
//#endif

#include "qmodbusasciiserialmaster.h"
#include "qmodbusasciiserialmaster_p.h"

#include <QtCore/qloggingcategory.h>


QT_BEGIN_NAMESPACE

Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS)
Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS_LOW)

/*!
    \class QModbusAsciiSerialMaster
    \inmodule QtSerialBus
    \since 5.6

    \brief The QModbusAsciiSerialMaster class represents a Modbus client
    that uses a serial bus for its communication with the Modbus server.

    Communication via Modbus requires the interaction between a single
    Modbus client instance and multiple Modbus servers. This class
    provides the client implementation via a serial port.
*/

/*!
    Constructs a serial Modbus master with the specified \a parent.
*/
QModbusAsciiSerialMaster::QModbusAsciiSerialMaster(QObject *parent)
    : QModbusClient(*new QModbusAsciiSerialMasterPrivate, parent)
{
    Q_D(QModbusAsciiSerialMaster);
    d->setupSerialPort();
}

/*!
    \internal
*/
QModbusAsciiSerialMaster::~QModbusAsciiSerialMaster()
{
    close();
}

/*!
    \since 5.7

    Returns the amount of microseconds for the silent interval between two
    consecutive Modbus messages.

    \sa setInterFrameDelay()
*/
int QModbusAsciiSerialMaster::interFrameDelay() const
{
    Q_D(const QModbusAsciiSerialMaster);
    return d->m_interFrameDelayMilliseconds * 1000;
}

/*!
    \since 5.7

    Sets the amount of \a microseconds for the silent interval between two
    consecutive Modbus messages. By default, the class implementation will use
    a pre-calculated value according to the Modbus specification. A active or
    running connection is not affected by such delay changes.

    \note If \a microseconds is set to -1 or \a microseconds is less than the
    pre-calculated delay then this pre-calculated value is used as frame delay.
*/
void QModbusAsciiSerialMaster::setInterFrameDelay(int microseconds)
{
    Q_D(QModbusAsciiSerialMaster);
    d->m_interFrameDelayMilliseconds = qCeil(qreal(microseconds) / 1000.);
    d->calculateInterFrameDelay();
}

/*!
    \internal
*/
QModbusAsciiSerialMaster::QModbusAsciiSerialMaster(QModbusAsciiSerialMasterPrivate &dd, QObject *parent)
    : QModbusClient(dd, parent)
{
    Q_D(QModbusAsciiSerialMaster);
    d->setupSerialPort();
}

/*!
     \reimp

     \note When calling this function, existing buffered data is removed from
     the serial port.
*/
bool QModbusAsciiSerialMaster::open()
{
    if (state() == QModbusDevice::ConnectedState)
        return true;

    Q_D(QModbusAsciiSerialMaster);
    //配置一些串口参数
    d->setupEnvironment(); // to be done before open
    if (d->m_serialPort->open(QIODevice::ReadWrite)) {
        setState(QModbusDevice::ConnectedState);
        d->m_serialPort->clear(); // only possible after open
    } else {
        setError(d->m_serialPort->errorString(), QModbusDevice::ConnectionError);
    }
    return (state() == QModbusDevice::ConnectedState);
}

/*!
     \reimp
*/
void QModbusAsciiSerialMaster::close()
{
    if (state() == QModbusDevice::UnconnectedState)
        return;

    setState(QModbusDevice::ClosingState);

    Q_D(QModbusAsciiSerialMaster);

    if (d->m_serialPort->isOpen())
        d->m_serialPort->close();

    if (d->m_queue.count())
        qCDebug(QT_MODBUS_LOW) << "(ASCII client) Aborted replies:" << d->m_queue.count();

    while (!d->m_queue.isEmpty()) {
        // Finish each open reply and forget them
        QModbusAsciiSerialMasterPrivate::QueueElement elem = d->m_queue.dequeue();
        if (!elem.reply.isNull()) {
            elem.reply->setError(QModbusDevice::ReplyAbortedError,
                                 QModbusClient::tr("Reply aborted due to connection closure."));
        }
    }

    setState(QModbusDevice::UnconnectedState);
}
